package com.mkx.aop;

import com.mkx.annotation.RedisLock;
import com.mkx.template.RedisLockTemplate;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;

@Aspect
public class RedisLockAspect {
    
    private static final Logger logger = LoggerFactory.getLogger(RedisLockAspect.class);
    
    private final RedisLockTemplate redisLockTemplate;
    
    public RedisLockAspect(RedisLockTemplate redisLockTemplate) {
        this.redisLockTemplate = redisLockTemplate;
    }
    
    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        String lockKey = parseKey(redisLock.key(), joinPoint);
        
        logger.debug("Attempting to acquire lock: {} with WatchDog strategy", lockKey);
        
        return redisLockTemplate.executeWithLock(
            lockKey,
            redisLock.waitTime(),
            redisLock.timeUnit(),
            () -> {
                try {
                    return joinPoint.proceed();
                } catch (RuntimeException e) {
                    throw e;
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
        );
    }
    
    private String parseKey(String key, ProceedingJoinPoint joinPoint) {
        if (key.contains("#")) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Object[] args = joinPoint.getArgs();
            String[] parameterNames = signature.getParameterNames();
            
            StandardEvaluationContext context = new StandardEvaluationContext();
            context.setVariable("args", args);
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
            
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(key);
            return expression.getValue(context, String.class);
        }
        return key;
    }
}